﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using TestcraftLibrary.Common;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Collections;

namespace Testcraft
{
    unsafe class ChunkPartEx2 : IDisposable
    {
        [Flags]
        public enum FaceMask : byte
        {
            PositiveX = 1,
            PositiveY = 2,
            PositiveZ = 4,
            NegativeX = 8,
            NegativeY = 16,
            NegativeZ = 32
        };

        private const float ExpireTimeout = 15.0f;
        private GraphicsDevice device;
        private const int UnindexedVertex = -1;

        public bool needsUpdate;

        public ushort[, ,] blocks;

        public byte[, ,] blockFaces;
        private ushort[] containedBlockTypes;
        public Dictionary<int, BlockBuffer> buffers;

        public BoundingBox Bounds;
        public short Width { get; private set; }
        public short Height { get; private set; }
        public Chunk Parent { get; private set; }
        public int Index { get; private set; }

#if DEBUG_METRICS
        public ulong VertexCount { get; private set; }
        public ulong IndexCount { get; private set; }
#endif


        public ConcurrentQueue<BlockDescriptorAdd> BlocksPendingAdd { get; private set; }
        public ConcurrentQueue<Block> BlocksPendingRemove { get; private set; }
        public OcclusionBlock occlusionBlock;
        public OcclusionQuery occlusionQuery;
        public bool isVisible;
        public float expireTimer;
        public int visibleQueryTimer;
        public bool untouched = true;
        public bool forceRefresh;
        public bool initialized = false;

        public int DisposeGuard = 1;
        public bool IsDisposed = false;


        public ChunkPartEx2(GraphicsDevice device, Chunk parent, int index, BoundingBox bounds, short width, short height)
        {
            if (device.IsDisposed)
                return;
            this.occlusionQuery = new OcclusionQuery(device);
            this.occlusionBlock = new OcclusionBlock(device, new Vector3((parent.X * width) + ((width / 2.0f) - 0.5f), (index * height) + ((height / 2.0f) - 0.5f), (parent.Y * width) + ((width / 2.0f) - 0.5f)), new Vector3(width, height, width));
            this.Parent = parent;
            this.Bounds = bounds;
            this.device = device;
            this.Width = width;
            this.Height = height;
            this.Index = index;
            this.blocks = null; // new Block[width, height, width];
            this.buffers = new Dictionary<int, BlockBuffer>();
            this.BlocksPendingAdd = new ConcurrentQueue<BlockDescriptorAdd>();
            this.BlocksPendingRemove = new ConcurrentQueue<Block>();

            this.containedBlockTypes = new ushort[Enum.GetNames(typeof(BlockType)).Length];

            this.device.DeviceReset += new EventHandler<EventArgs>(device_DeviceReset);
            this.InitializeMatrices();
        }

        void device_DeviceReset(object sender, EventArgs e)
        {
            this.forceRefresh = true;
            this.Update();
        }

        private void InitializeMatrices()
        {
            this.blocks = new ushort[this.Width, this.Height, this.Width];

            this.blockFaces = new byte[this.Width, this.Height, this.Width];
            for (int x = 0; x < this.Width; x++)
            {
                for (int y = 0; y < this.Height; y++)
                {
                    for (int z = 0; z < this.Width; z++)
                    {
                        this.blocks[x, y, z] = Block.AirID;
                        this.blockFaces[x, y, z] = 0;
                    }
                }
            }
        }

        Random rand = new Random();
        public void SetFaceVisibility(ChunkPartEx2 chunkPart, short x, short y, short z, Direction dir, bool visible)
        {
            if (this.blocks[x, y, z] == Block.UninitializedBlockID)
            {
                this.blocks[x, y, z] = (ushort)rand.Next(0, 2);
            }

            FaceMask mask = this.GetFaceMaskFromDirection(dir);

            byte oldFacesState = chunkPart.blockFaces[x, y, z];

            if (visible)
                chunkPart.blockFaces[x, y, z] |= (byte)mask;
            else
                chunkPart.blockFaces[x, y, z] &= (byte)~mask;

            //if (chunkPart.blockFaces[x, y, z] != oldFacesState)
            //    chunkPart.forceRefresh = true;
        }

        private FaceMask GetFaceMaskFromDirection(Direction dir)
        {
            FaceMask mask = (FaceMask)0;
            switch (dir)
            {
                case Direction.PositiveX:
                    mask = FaceMask.PositiveX;
                    break;
                case Direction.PositiveY:
                    mask = FaceMask.PositiveY;
                    break;
                case Direction.PositiveZ:
                    mask = FaceMask.PositiveZ;
                    break;
                case Direction.NegativeX:
                    mask = FaceMask.NegativeX;
                    break;
                case Direction.NegativeY:
                    mask = FaceMask.NegativeY;
                    break;
                case Direction.NegativeZ:
                    mask = FaceMask.NegativeZ;
                    break;
            }

            return mask;
        }

        public void Initialize()
        {
            Stopwatch watch = Stopwatch.StartNew();
            this.UpdateBlockMatrix();

            this.UpdateFaceMatrix();

            this.UpdateBuffersUsingCompositeFaces();

            this.initialized = true;
            watch.Stop();
            //Console.WriteLine("Initialize: {0}MS", watch.ElapsedMilliseconds);
        }

        public void Update()
        {
            if (this.initialized)
                this.InternalUpdate();
        }

        private void ResetIndexLookup(short[, , ,] lookup)
        {
            for (int x = 0; x < this.Width + 1; x++)
            {
                for (int y = 0; y < this.Height + 1; y++)
                {
                    for (int z = 0; z < this.Width + 1; z++)
                    {
                        for (int dir = 0; dir < 6; dir++)
                        {
                            lookup[x, y, z, dir] = UnindexedVertex;
                        }
                    }
                }
            }
        }

        uint asd = 0;
        private void InternalUpdate()
        {
            if (this.BlocksPendingAdd.Count > 0 || this.BlocksPendingRemove.Count > 0)
            {
                this.UpdateBlockMatrix();
                this.needsUpdate = true;
            }

            if (this.needsUpdate)
            {
                this.UpdateFaceMatrix();
                this.UpdateBuffersUsingCompositeFaces();
                this.needsUpdate = false;

                //this.ApplyFaceChanges();
            }

        }

        //private bool IsAirBlockClosedBySolidBlocks(short x, short y, short z)
        //{
        //    bool closed = true;
        //    Block neighbor = null;
        //    for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
        //    {
        //        if ((!TryGetBlockFromDirection(x, y, z, dir, out neighbor) || neighbor == null || neighbor.Transparent))
        //        {
        //            closed = false;
        //            break;
        //        }
        //    }

        //    return closed;
        //}

        private bool WillAirBlockBeClosedByNewBlock(ChunkPartEx2 chunkpart, short x, short y, short z, Direction directionToNewBlock)
        {
            bool closed = true;
            short neighborX, neighborY, neighborZ;
            ChunkPartEx2 neighborChunkPart;
            for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
            {
                if (dir != directionToNewBlock)
                {
                    this.GetLocationByDirection(x, y, z, dir, out neighborX, out neighborY, out neighborZ);
                    bool isNeighborInsideChunkPart = this.IsCoordinateWithinBounds(neighborX, neighborY, neighborZ);
                    if (isNeighborInsideChunkPart)
                    {
                        neighborChunkPart = chunkpart;
                    }
                    else
                    {
                        neighborChunkPart = this.GetChunkPartByDirection(chunkpart, dir);
                        if (neighborChunkPart != null)
                        {
                            if (neighborX < 0)
                                neighborX = 15;
                            else if (neighborX > 15)
                                neighborX = 0;

                            if (neighborY < 0)
                                neighborY = 15;
                            else if (neighborY > 15)
                                neighborY = 0;

                            if (neighborZ < 0)
                                neighborZ = 15;
                            else if (neighborZ > 15)
                                neighborZ = 0;
                        }
                    }

                    if (neighborChunkPart == null || neighborChunkPart.blocks[neighborX, neighborY, neighborZ] == Block.AirID || this.IsBlockTransparent(neighborChunkPart.blocks[neighborX, neighborY, neighborZ]))
                    {
                        closed = false;
                        break;
                    }
                }
            }

            return closed;
        }

        private void SetVisibilityOfAdjacentFaces(ChunkPartEx2 chunkpart, short x, short y, short z, bool visibility, bool updateFaces)
        {
            short neighborX, neighborY, neighborZ;
            ChunkPartEx2 neighborChunkPart;
            for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
            {

                this.GetLocationByDirection(x, y, z, dir, out neighborX, out neighborY, out neighborZ);
                bool isNeighborInsideChunkPart = this.IsCoordinateWithinBounds(neighborX, neighborY, neighborZ);
                if (isNeighborInsideChunkPart)
                {
                    neighborChunkPart = chunkpart;
                }
                else
                {
                    neighborChunkPart = this.GetChunkPartByDirection(chunkpart, dir);
                    if (neighborChunkPart != null)
                    {
                        if (neighborX < 0)
                            neighborX = 15;
                        else if (neighborX > 15)
                            neighborX = 0;

                        if (neighborY < 0)
                            neighborY = 15;
                        else if (neighborY > 15)
                            neighborY = 0;

                        if (neighborZ < 0)
                            neighborZ = 15;
                        else if (neighborZ > 15)
                            neighborZ = 0;
                    }
                }
                if (neighborChunkPart != null && neighborChunkPart.blocks[neighborX, neighborY, neighborZ] != Block.AirID)
                {
                    this.SetFaceVisibility(neighborChunkPart, neighborX, neighborY, neighborZ, this.GetOppositeDirection(dir), visibility);
                }
            }
        }

        public static void GetBlockCrossChunk(short x, short y, short z)
        {
            int chunkOffsetX = 0;
            int chunkoffsetY = 0;
            if (x >= /*this.Width*/16)
            {
                int newX;
                chunkOffsetX = Math.DivRem((int)x, /*(int)this.Width*/16, out newX);
                x = (short)newX;
            }
            else if (x < 0)
            {
                int newX;
                chunkOffsetX = -Math.DivRem((int)-x + 16, /*(int)this.Width*/16, out newX);
                if (newX == 0)
                    x = 0;
                else
                    x = (short)(16 - newX);

                //Console.WriteLine("Chunk {0}, X {1}", chunkOffsetX, x);
            }
        }

        private bool IsBlockTransparent(ushort id)
        {
            return false;
        }

        private bool IsCoordinateWithinBounds(short x, short y, short z)
        {
            return x >= 0 && y >= 0 && z >= 0 && x < this.Width && y < this.Height && z < this.Width;
        }

        private ChunkPartEx2 GetChunkPartByDirection(ChunkPartEx2 chunkpart, Direction dir)
        {
            ChunkPartEx2 result = null;
            if (dir == Direction.PositiveY)
            {
                if (chunkpart.Index < 15)
                    result = chunkpart.Parent.partsEx2[chunkpart.Index + 1];
            }
            else if (dir == Direction.NegativeY)
            {
                if (chunkpart.Index > 0)
                    result = chunkpart.Parent.partsEx2[chunkpart.Index - 1];
            }
            else
            {
                Chunk neighborChunk = null;

                if (dir == Direction.NegativeX)
                    neighborChunk = this.Parent.LeftNeighbor;
                else if (dir == Direction.PositiveX)
                    neighborChunk = this.Parent.RightNeighbor;
                else if (dir == Direction.NegativeZ)
                    neighborChunk = this.Parent.BackNeighbor;
                else if (dir == Direction.PositiveZ)
                    neighborChunk = this.Parent.FrontNeighbor;

                if (neighborChunk == null)
                    result = null;
                else
                    result = neighborChunk.partsEx2[chunkpart.Index];
            }

            return result;
        }

        public void UpdateBlockMatrix()
        {
            fixed (ushort* blockPtr = this.blocks)
            {
                BlockDescriptorAdd descriptor;
                while (this.BlocksPendingAdd.TryDequeue(out descriptor))
                {
                    if (descriptor.Type != Block.AirID && descriptor.Type != Block.UninitializedBlockID)
                        this.containedBlockTypes[descriptor.Type]++;

                    short blockX = (short)(descriptor.Location.X % this.Width);
                    short blockY = (short)(descriptor.Location.Y % this.Height);
                    short blockZ = (short)(descriptor.Location.Z % this.Width);

                    //if (this.blocks[blockX, blockY, blockZ] != Block.AirID)
                    //    continue;

                    //if (descriptor.Type == Block.UninitializedBlockID)
                    //{
                    //    this.blocks[blockX, blockY, blockZ] = Block.UninitializedBlockID;
                    //    this.SetVisibilityOfAdjacentFaces(this, blockX, blockY, blockZ, false, false);
                    //    continue;
                    //}

                    //this.blocks[blockX, blockY, blockZ] = descriptor.Type;
                    *(blockPtr + ((blockX * this.Height + blockY) * this.Width + blockZ)) = descriptor.Type;

                }

                Block block;
                while (this.BlocksPendingRemove.TryDequeue(out block))
                {
                    this.containedBlockTypes[block.Id]--;

                    short blockX = (short)(block.X % this.Width);
                    short blockY = (short)(block.Y % this.Height);
                    short blockZ = (short)(block.Z % this.Width);

                    if (this.blocks[blockX, blockY, blockZ] == Block.AirID)
                        continue;

                    for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
                    {
                        short neighborX, neighborY, neighborZ;
                        this.GetLocationByDirection(blockX, blockY, blockZ, dir, out neighborX, out neighborY, out neighborZ);
                        bool neighborIsInChunk = this.IsCoordinateWithinBounds(neighborX, neighborY, neighborZ);

                        ChunkPartEx2 neighborChunkPart;
                        if (neighborIsInChunk)
                        {
                            neighborChunkPart = this;
                        }
                        else
                        {
                            neighborChunkPart = this.GetChunkPartByDirection(this, dir);
                            if (neighborChunkPart != null)
                            {
                                if (neighborX < 0)
                                    neighborX = 15;
                                else if (neighborX > 15)
                                    neighborX = 0;

                                if (neighborY < 0)
                                    neighborY = 15;
                                else if (neighborY > 15)
                                    neighborY = 0;

                                if (neighborZ < 0)
                                    neighborZ = 15;
                                else if (neighborZ > 15)
                                    neighborZ = 0;
                            }
                        }

                        if (neighborChunkPart != null)
                        {
                            if (neighborChunkPart.blocks[neighborX, neighborY, neighborZ] == Block.UninitializedBlockID)
                            {
                                neighborChunkPart.GenerateBlock(neighborX, neighborY, neighborZ);
                            }
                            neighborChunkPart.needsUpdate = true;
                        }
                    }




                    this.blocks[blockX, blockY, blockZ] = Block.AirID;
                }
            }
        }

        private void GenerateBlock(short x, short y, short z)
        {
            BlockDescriptorAdd descriptor;
            descriptor.Type = (ushort)BlockType.Grass;
            descriptor.Location.X = x;
            descriptor.Location.Y = y;
            descriptor.Location.Z = z;
            this.blocks[x, y, z] = (ushort)BlockType.Grass;
            //this.BlocksPendingAdd.Add(descriptor);
            //this.BlocksPendingAdd.Add(new Block((ushort)BlockType.Grass) { X = x, Y = y, Z = z });
        }

        public void UpdateFaceMatrix()
        {
            for (short blockX = 0; blockX < this.Width; blockX++)
            {
                for (short blockY = 0; blockY < this.Height; blockY++)
                {
                    for (short blockZ = 0; blockZ < this.Width; blockZ++)
                    {
                        if (this.blocks[blockX, blockY, blockZ] != Block.AirID &&
                            this.blocks[blockX, blockY, blockZ] != Block.UninitializedBlockID)
                        {

                            ChunkPartEx2 neighborChunkPart;
                            short neighborX, neighborY, neighborZ;
                            for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
                            {
                                this.GetLocationByDirection(blockX, blockY, blockZ, dir, out neighborX, out neighborY, out neighborZ);

                                bool neighborIsInChunk = this.IsCoordinateWithinBounds(neighborX, neighborY, neighborZ);

                                if (neighborIsInChunk)
                                {
                                    neighborChunkPart = this;
                                }
                                else
                                {
                                    // (this, dir) ??
                                    neighborChunkPart = this.GetChunkPartByDirection(this, dir);
                                    if (neighborChunkPart != null)
                                    {
                                        if (neighborX < 0)
                                            neighborX = 15;
                                        else if (neighborX > 15)
                                            neighborX = 0;

                                        if (neighborY < 0)
                                            neighborY = 15;
                                        else if (neighborY > 15)
                                            neighborY = 0;

                                        if (neighborZ < 0)
                                            neighborZ = 15;
                                        else if (neighborZ > 15)
                                            neighborZ = 0;
                                    }
                                }

                                if (neighborChunkPart == null)
                                {
                                    this.SetFaceVisibility(this, blockX, blockY, blockZ, dir, true);
                                }
                                else
                                {
                                    if (neighborChunkPart.blocks[neighborX, neighborY, neighborZ] == Block.AirID)
                                    {
                                        if (this.WillAirBlockBeClosedByNewBlock(neighborChunkPart, neighborX, neighborY, neighborZ, this.GetOppositeDirection(dir)))
                                        {
                                            this.SetVisibilityOfAdjacentFaces(neighborChunkPart, neighborX, neighborY, neighborZ, false, false);
                                        }
                                        else
                                        {
                                            this.SetFaceVisibility(this, blockX, blockY, blockZ, dir, true);
                                        }
                                    }
                                    else if (this.IsBlockTransparent(neighborChunkPart.blocks[neighborX, neighborY, neighborZ]))
                                    {
                                        this.SetFaceVisibility(this, blockX, blockY, blockZ, dir, true);
                                    }
                                    else
                                    {
                                        this.SetFaceVisibility(neighborChunkPart, neighborX, neighborY, neighborZ, this.GetOppositeDirection(dir), false);
                                        this.SetFaceVisibility(this, blockX, blockY, blockZ, dir, false);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        public void UpdateBuffersUsingCompositeFaces()
        {
            Dictionary<int, List<PackedVertexPositionNormalTexture>> verticeDictionary = new Dictionary<int, List<PackedVertexPositionNormalTexture>>();
            Dictionary<int, List<Int16>> indiceDictionary = new Dictionary<int, List<Int16>>();
            List<PackedVertexPositionNormalTexture> blockVertices;
            List<Int16> blockIndices;
            FaceMask mask;

            for (ushort blockType = 0; blockType < 2; blockType++)
            {
                for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
                {

                    int textureIndex = ChunkEngine.BlockTextureLookup[blockType, (int)dir];
                    if (verticeDictionary.TryGetValue(textureIndex, out blockVertices))
                    {
                        blockIndices = indiceDictionary[textureIndex];
                        //indexLookup = lookupDictionary[textureIndex];
                    }
                    else
                    {
                        verticeDictionary.Add(textureIndex, blockVertices = new List<PackedVertexPositionNormalTexture>());
                        indiceDictionary.Add(textureIndex, blockIndices = new List<Int16>());
                        //lookupDictionary.Add(textureIndex, indexLookup = new short[this.Width + 1, this.Height + 1, this.Width + 1, 6]);
                        //this.ResetIndexLookup(indexLookup);
                    }


                    for (int x = 0; x < 16; x++)
                    {
                        for (int y = 0; y < 16; y++)
                        {
                            for (int z = 0; z < 16; z++)
                            {
                                if (this.blocks[x, y, z] == blockType)
                                {
                                    mask = this.GetFaceMaskFromDirection(dir);

                                    if ((this.blockFaces[x, y, z] & (byte)mask) == (byte)mask)
                                    {
                                        int x2 = x;
                                        int y2 = y;
                                        int z2 = z;

                                        bool stop = false;

                                        switch (dir)
                                        {
                                            case Direction.PositiveX:
                                            case Direction.NegativeX:
                                                while (++y2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask))
                                                    this.blockFaces[x2, y2, z2] &= (byte)~(byte)mask;
                                                y2--;
                                                while (++z2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask))
                                                    this.blockFaces[x2, y2, z2] &= (byte)~(byte)mask;
                                                
                                                z2--;
                                                break;
                                            case Direction.PositiveZ:
                                            case Direction.NegativeZ:
                                                while (++y2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask))
                                                    this.blockFaces[x2, y2, z2] &= (byte)~(byte)mask;
                                                y2--;
                                                while (!stop && ++x2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask))
                                                {
                                                    for (int yy = y; yy <= y2; yy++)
                                                    {
                                                        if (this.blocks[x2, yy, z2] != blockType || ((this.blockFaces[x2, yy, z2] & (byte)mask) != (byte)mask))
                                                        {
                                                            stop = true;
                                                            break;
                                                        }
                                                    }
                                                }
                                                x2--;

                                                for (int xx = x; xx <= x2; xx++)
                                                {
                                                    for (int yy = y; yy <= y2; yy++)
                                                    {
                                                        this.blockFaces[xx, yy, z2] &= (byte)~(byte)mask;
                                                    }
                                                }
                                                break;
                                            case Direction.PositiveY:
                                            case Direction.NegativeY:
                                                while (++z2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask));
                                                z2--;
                                                while (!stop && ++x2 < 16 && this.blocks[x2, y2, z2] == blockType && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask))
                                                {
                                                    for (int zz = z; zz <= z2; zz++)
                                                    {
                                                        if (this.blocks[x2, y2, zz] != blockType || ((this.blockFaces[x2, y2, zz] & (byte)mask) != (byte)mask))
                                                        {
                                                            stop = true;
                                                            break;
                                                        }
                                                    }
                                                }
                                                x2--;

                                                for (int xx = x; xx <= x2; xx++)
                                                {
                                                    for (int zz = z; zz <= z2; zz++)
                                                    {
                                                        this.blockFaces[xx, y2, zz] &= (byte)~(byte)mask;
                                                    }
                                                }
                                                break;
                                        }

                                        //while (x2++ < 16 && ((this.blockFaces[x2, y2, z2] & (byte)mask) == (byte)mask)) ;

                                        int xspan = x2 - x;
                                        int yspan = y2 - y;
                                        int zspan = z2 - z;

                                        this.ShowCompositeBlockFace2(blockVertices, blockIndices, x, y, z, xspan, yspan, zspan, dir);

                                        z += zspan;
                                    }
                                }
                            }
                        }
                    }



                }

            }

            System.Threading.Monitor.Enter(this.buffers);
            foreach (KeyValuePair<int, List<Int16>> textureIndices in indiceDictionary)
            {
                if (verticeDictionary[textureIndices.Key].Count > 0)
                {
                    BlockBuffer buffer;
                    if (!this.buffers.TryGetValue(textureIndices.Key, out buffer))
                    {
                        buffer = new BlockBuffer(textureIndices.Key, this.device, this.Width, this.Height);
                        this.buffers[textureIndices.Key] = buffer;
                    }
#if DEBUG_METRICS
                    this.VertexCount += (ulong)verticeDictionary[textureIndices.Key].Count;
                    this.IndexCount += (ulong)textureIndices.Value.Count;
#endif

                    //buffer.RebuildVBuffer(blockVertices);
                    //System.Threading.Monitor.Enter(buffer);
                    buffer.Rebuild(verticeDictionary[textureIndices.Key], textureIndices.Value);
                    //System.Threading.Monitor.Exit(buffer);
                }
            }
            System.Threading.Monitor.Exit(this.buffers);
        }

        public void UpdateBuffers()
        {
            System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
#if DEBUG_METRICS
            this.VertexCount = 0;
            this.IndexCount = 0;
#endif
            FaceMask mask;
            short[, , ,] indexLookup;// = new short[this.Width + 1, this.Height + 1, this.Width + 1, 6];
            Dictionary<int, List<PackedVertexPositionNormalTexture>> verticeDictionary = new Dictionary<int, List<PackedVertexPositionNormalTexture>>();
            Dictionary<int, List<Int16>> indiceDictionary = new Dictionary<int, List<Int16>>();
            Dictionary<int, short[, , ,]> lookupDictionary = new Dictionary<int, short[, , ,]>();
            for (int blockType = 0; blockType < 2; blockType++)
            {
                if (this.containedBlockTypes[blockType] == 0)
                    continue;
                for (Direction dir = Direction.PositiveX; dir <= Direction.NegativeZ; dir++)
                {

                    List<Int16> blockIndices;
                    //this.ResetIndexLookup(indexLookup);

                    int textureIndex = ChunkEngine.BlockTextureLookup[blockType, (int)dir];
                    List<PackedVertexPositionNormalTexture> blockVertices;
                    if (verticeDictionary.TryGetValue(textureIndex, out blockVertices))
                    {
                        blockIndices = indiceDictionary[textureIndex];
                        indexLookup = lookupDictionary[textureIndex];
                    }
                    else
                    {
                        verticeDictionary.Add(textureIndex, blockVertices = new List<PackedVertexPositionNormalTexture>());
                        indiceDictionary.Add(textureIndex, blockIndices = new List<Int16>());
                        lookupDictionary.Add(textureIndex, indexLookup = new short[this.Width + 1, this.Height + 1, this.Width + 1, 6]);
                        this.ResetIndexLookup(indexLookup);
                    }
                    for (int x = 0; x < 16; x++)
                    {
                        for (int y = 0; y < 16; y++)
                        {
                            for (int z = 0; z < 16; z++)
                            {
                                if (this.blocks[x, y, z] == blockType)
                                {
                                    mask = this.GetFaceMaskFromDirection(dir);
                                    if ((this.blockFaces[x, y, z] & (byte)mask) == (byte)mask)
                                    {
                                        ShowCompositeBlockFace2(blockVertices, blockIndices, x, y, z, 0, 0, 0, dir);
                                        //this.ShowBlockFace(blockVertices, blockIndices, indexLookup, x, y, z, dir);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            System.Threading.Monitor.Enter(this.buffers);
            foreach (KeyValuePair<int, List<Int16>> textureIndices in indiceDictionary)
            {
                if (verticeDictionary[textureIndices.Key].Count > 0)
                {
                    BlockBuffer buffer;
                    if (!this.buffers.TryGetValue(textureIndices.Key, out buffer))
                    {
                        buffer = new BlockBuffer(textureIndices.Key, this.device, this.Width, this.Height);
                        this.buffers[textureIndices.Key] = buffer;
                    }
#if DEBUG_METRICS
                    this.VertexCount += (ulong)verticeDictionary[textureIndices.Key].Count;
                    this.IndexCount += (ulong)textureIndices.Value.Count;
#endif

                    //buffer.RebuildVBuffer(blockVertices);
                    //System.Threading.Monitor.Enter(buffer);
                    buffer.Rebuild(verticeDictionary[textureIndices.Key], textureIndices.Value);
                    //System.Threading.Monitor.Exit(buffer);
                }
            }
            System.Threading.Monitor.Exit(this.buffers);

            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
            //Console.WriteLine("---({1}:{2}:{3}) (forcerefresh: {4}) Elapsed: {0}.", watch.ElapsedMilliseconds, this.Parent.X, this.Parent.Y, this.Index, this.forceRefresh);

        }

        private Direction GetOppositeDirection(Direction direction)
        {
            switch (direction)
            {
                case Direction.PositiveX:
                    return Direction.NegativeX;
                case Direction.NegativeX:
                    return Direction.PositiveX;
                case Direction.PositiveY:
                    return Direction.NegativeY;
                case Direction.NegativeY:
                    return Direction.PositiveY;
                case Direction.PositiveZ:
                    return Direction.NegativeZ;
                case Direction.NegativeZ:
                    return Direction.PositiveZ;
                default:
                    throw new Exception("Detta ska inte kunna hända!!");
            }
        }

        private void ShowCompositeBlockFace2(List<PackedVertexPositionNormalTexture> vertices, List<Int16> indices, int x, int y, int z, int xspan, int yspan, int zspan, Direction face)
        {
            int chunkPartX = x;
            int chunkPartY = y;
            int chunkPartZ = z;

            Vector3 offset = new Vector3(this.Parent.X * Chunk.ChunkWidth, this.Index * this.Height, this.Parent.Y * Chunk.ChunkWidth) +
                (new Vector3((float)rand.NextDouble(), (float)rand.NextDouble(), (float)rand.NextDouble()) * 0.0f);

            short topLeftIndex, topRightIndex, lowerLeftIndex, lowerRightIndex;
            //int topLeftIndex.index, topRightIndex, lowerLeftIndex, lowerRightIndex;
            switch (face)
            {
                case Direction.PositiveX:

                    // Block to the right does not exist or is transparent, so our blocks rightmost face is visible.

                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ + 0.5f + zspan) + offset, Vector3.Right, new Vector2(this.Width - (chunkPartZ + zspan) - 1, this.Height - (chunkPartY + yspan) - 1)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ - 0.5f) + offset, Vector3.Right, new Vector2(this.Width - chunkPartZ, this.Height - (chunkPartY + yspan) - 1)));

                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Right, new Vector2(this.Width - (chunkPartZ + zspan) - 1, this.Height - chunkPartY)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Right, new Vector2(this.Width - chunkPartZ, this.Height - chunkPartY)));

                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
                case Direction.NegativeX:
                    // Block to the left does not exist or is transparent, so our blocks leftmost face is visible.


                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ - 0.5f) + offset, Vector3.Left, new Vector2(chunkPartZ, this.Height - (chunkPartY + yspan) - 1)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ + 0.5f + zspan) + offset, Vector3.Left, new Vector2(chunkPartZ + zspan + 1, this.Height - (chunkPartY + yspan) - 1)));

                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Left, new Vector2(chunkPartZ, this.Height - chunkPartY)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Left, new Vector2(chunkPartZ + zspan + 1, this.Height - chunkPartY)));

                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
                case Direction.PositiveY:
                    // Block above does not exist or is transparent, so our blocks topmost face is visible.


                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Up, new Vector2(chunkPartX, chunkPartZ)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY + 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Up, new Vector2(chunkPartX + 1 + xspan, chunkPartZ)));
                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Up, new Vector2(chunkPartX, chunkPartZ + zspan + 1)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY + 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Up, new Vector2(chunkPartX + 1 + xspan, chunkPartZ + 1 + zspan)));

                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
                case Direction.NegativeY:
                    // Block below does not exist or is transparent, so our blocks bottommost face is visible.

                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Down, new Vector2(chunkPartX, this.Width - (chunkPartZ + zspan) - 1)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY - 0.5f, chunkPartZ + 0.5f + zspan) + offset, Vector3.Down, new Vector2(chunkPartX + xspan + 1, this.Width - (chunkPartZ + zspan) - 1)));

                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Down, new Vector2(chunkPartX, this.Width - chunkPartZ)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Down, new Vector2(chunkPartX + xspan + 1, this.Width - chunkPartZ)));

                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
                case Direction.PositiveZ:
                    // Block in front does not exist or is transparent, so our blocks front face is visible.

                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ + 0.5f) + offset, Vector3.Backward, new Vector2(chunkPartX, this.Height - (chunkPartY + yspan) - 1)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY + 0.5f + yspan, chunkPartZ + 0.5f) + offset, Vector3.Backward, new Vector2(chunkPartX + xspan + 1, this.Height - (chunkPartY + yspan) - 1)));

                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f) + offset, Vector3.Backward, new Vector2(chunkPartX, this.Height - chunkPartY)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY - 0.5f, chunkPartZ + 0.5f) + offset, Vector3.Backward, new Vector2(chunkPartX + xspan + 1, this.Height - chunkPartY)));


                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
                case Direction.NegativeZ:
                    // Block in back does not exist or is transparent, so our blocks back face is visible.

                    topLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY + 0.5f + yspan, chunkPartZ - 0.5f) + offset, Vector3.Forward, new Vector2(this.Width - (chunkPartX + xspan) - 1, this.Height - (chunkPartY + yspan) - 1)));

                    topRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f + yspan, chunkPartZ - 0.5f) + offset, Vector3.Forward, new Vector2(this.Width - chunkPartX, this.Height - (chunkPartY + yspan) - 1)));

                    lowerLeftIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f + xspan, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Forward, new Vector2(this.Width - (chunkPartX + xspan) - 1, this.Height - chunkPartY)));

                    lowerRightIndex = (short)vertices.Count;

                    vertices.Add(new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f) + offset, Vector3.Forward, new Vector2(this.Width - chunkPartX, this.Height - chunkPartY)));

                    indices.AddRange(new short[] { topLeftIndex, topRightIndex, lowerLeftIndex, topRightIndex, lowerRightIndex, lowerLeftIndex });

                    break;
            }


        }

        private void ShowBlockFace(List<PackedVertexPositionNormalTexture> vertices, int x, int y, int z, Direction face, int foo)
        {
            int chunkPartX = x;
            int chunkPartY = y;
            int chunkPartZ = z;

            //if (block.faceIndices[(int)face] > -1)
            //    return;

            PackedVertexPositionNormalTexture upperLeft, upperRight, lowerLeft, lowerRight;
            //int topLeftIndex.index, topRightIndex, lowerLeftIndex, lowerRightIndex;
            switch (face)
            {
                case Direction.PositiveX:

                    upperLeft = new PackedVertexPositionNormalTexture(
                            new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Right, new Vector2(0.5f, 0.75f));

                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Right, new Vector2(0.5f, 0.5f));

                    lowerLeft = new PackedVertexPositionNormalTexture(
                            new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Right, new Vector2(0.75f, 0.75f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Right, new Vector2(0.75f, 0.5f));
                    break;
                case Direction.NegativeX:
                    upperLeft = new PackedVertexPositionNormalTexture(
                            new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Left, new Vector2(0.25f, 0.5f));


                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Left, new Vector2(0.25f, 0.75f));

                    lowerLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Left, new Vector2(0.0f, 0.5f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Left, new Vector2(0.0f, 0.75f));

                    break;
                case Direction.PositiveY:

                    upperLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Up, new Vector2(0.25f, 0.5f));

                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Up, new Vector2(0.5f, 0.5f));

                    lowerLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Up, new Vector2(0.25f, 0.75f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Up, new Vector2(0.5f, 0.75f));

                    break;
                case Direction.NegativeY:

                    upperLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Down, new Vector2(0.5f, 0.25f));

                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Down, new Vector2(0.25f, 0.25f));


                    lowerLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Down, new Vector2(0.5f, 0.0f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Down, new Vector2(0.25f, 0.0f));


                    break;
                case Direction.PositiveZ:

                    upperLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Backward, new Vector2(0.25f, 0.75f));

                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ + 0.5f), Vector3.Backward, new Vector2(0.5f, 0.75f));

                    lowerLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Backward, new Vector2(0.25f, 1.0f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ + 0.5f), Vector3.Backward, new Vector2(0.5f, 1.0f));

                    break;
                case Direction.NegativeZ:

                    upperLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Forward, new Vector2(0.5f, 0.5f));

                    upperRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY + 0.5f, chunkPartZ - 0.5f), Vector3.Forward, new Vector2(0.25f, 0.5f));


                    lowerLeft = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX + 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Forward, new Vector2(0.5f, 0.25f));

                    lowerRight = new PackedVertexPositionNormalTexture(
                        new Vector3(chunkPartX - 0.5f, chunkPartY - 0.5f, chunkPartZ - 0.5f), Vector3.Forward, new Vector2(0.25f, 0.25f));
                    break;
                default:
                    throw new Exception("WUTWUTWUTWUT");
            }

            //buffer.flags |= BlockBuffer.BufferFlags.VBufferDirty;
            //block.faceIndices[(int)face] = (short)buffer.vbufferContents.Count;
            //buffer.indexBufferLookup[buffer.vbufferContents.Count] = block;

            // Calculate offset based on chunk world position.
            Vector3 offset = new Vector3(this.Parent.X * Chunk.ChunkWidth, this.Index * this.Height, this.Parent.Y * Chunk.ChunkWidth);
            upperLeft.Position += offset;
            upperRight.Position += offset;
            lowerLeft.Position += offset;
            lowerRight.Position += offset;


            vertices.AddRange(new PackedVertexPositionNormalTexture[] { upperLeft, upperRight, lowerLeft, upperRight, lowerRight, lowerLeft });
            //buffer.vbufferContents.AddRange(new PackedVertexPositionNormalTexture[] { upperLeft, upperRight, lowerLeft, upperRight, lowerRight, lowerLeft });

        }

        private void GetLocationByDirection(short x, short y, short z, Direction dir, out short resultX, out short resultY, out short resultZ)
        {
            switch (dir)
            {
                case Direction.PositiveX:
                    x += 1;
                    break;
                case Direction.NegativeX:
                    x -= 1;
                    break;
                case Direction.PositiveY:
                    y += 1;
                    break;
                case Direction.NegativeY:
                    y -= 1;
                    break;
                case Direction.PositiveZ:
                    z += 1;
                    break;
                case Direction.NegativeZ:
                    z -= 1;
                    break;

            }

            resultX = x;
            resultY = y;
            resultZ = z;
        }

        //private bool TryGetBlockFromDirection(short x, short y, short z, Direction dir, out Block result)
        //{
        //    bool success;
        //    this.GetLocationByDirection(x, y, z, dir, out x, out y, out z);

        //    if (x < 0 || x >= this.Width ||
        //        y < 0 || y >= this.Height ||
        //        z < 0 || z >= this.Width)
        //    {
        //        success = false;
        //        result = null;
        //    }
        //    else
        //    {
        //        success = true;
        //        result = this.blocks[x, y, z];
        //    }

        //    return success;
        //}

        public void Dispose()
        {
            this.device.DeviceReset -= this.device_DeviceReset;

            this.IsDisposed = true;
            System.Threading.Monitor.Enter(this.buffers);
            foreach (BlockBuffer buffer in this.buffers.Values)
            {
                System.Threading.Monitor.Enter(buffer);
                buffer.Dispose();
                System.Threading.Monitor.Exit(buffer);
            }
            this.buffers.Clear();
            System.Threading.Monitor.Exit(this.buffers);

            this.occlusionQuery.Dispose();
        }
    }
}
